#include <algorithm>
#include <chrono>
#include <iostream>
#include <type_traits>
#include <unordered_map>
#include <vector>
#include <iterator>
#include <optional>
#include <ranges>

// import merchant, not necessary cause exported by store
import customer;
import store;

using namespace std::chrono;
using namespace std::literals::chrono_literals;
using namespace std::ranges;

using Stores = std::vector<const Store *>;

// methods
Stores get_favorite_stores_for(const CustomerId &cid) {
  static const auto merchants = std::vector<Merchant>{{17}, {29}}; // two struct

  static const auto sts = std::vector<Store>{
      {.owner = &merchants[0],

       .items =
           {
               {.name = "honey",
                .photo_url = {},
                .description = "straight outta compton's apiary",
                .price = 9.99f,
                .date_added = system_clock::now(),
                .featured = false},
               {.name = "Oscypek",
                .photo_url = {},
                .description = "Tasty smoked cheese from the tetra mountains",
                .price = 1.23f,
                .date_added = system_clock::now() - 1h,
                .featured = true},
           },

       .categories = {Category::Food}
      },

       {.owner = &merchants[1],

        .ietms =
            {{.name = "Handmade painted ceramic bowls",
              .photo_url = "http://example.com/beautiful_bowl.png",
              .description =
                  "Hand-crafted and hand-decorated bowls made of fired clay",
              .price = {},
              .date_added = system_clock::now() - 12min,
              .featured = true}},

        .categories = {Category::Artist, Category::Handicraft}
       }
  };
  

  static auto favorite_stores_by_customer =
      std::unordered_map<CustomerId, Stores>{{42, {&sts[0], &sts[1]}}};

  return favorite_stores_by_customer[cid];
}

using Items = std::vector<const Item *>;
// NOTE: modify on methods
auto get_featured_items_for_store(const Store &store) {
  return store.items | views::filter(&Item::featured) |
         views::transform([](const auto &itm) { return &itm; });
}

range auto get_all_featured_items(const Stores &sts) {
  auto all_featured = sts | views::transform([](auto elem) {
                        return get_featured_items_for_store(*elem);
                      }) |
                      views::join;

  auto as_items = Items{};

  as_items.reserve(distance(all_featured));
  copy(all_featured, std::back_inserter(as_items));

  return as_items;
}

template <typename Range, typename Comp, typename Proj>
concept sortable_range =
    random_access_range<Range> && std::sortable<iterator_t<Range>, Comp, Proj>;

void order_items_by_date_added(
    sortable_range<greater, decltype(&Item::date_added)> auto &itms) {
  sort(itms, greater{}, &Item::date_added);
}

template <input_range Container>
  requires std::is_same_v<typename Container::value_type, const Item *>
void render_item_gallery(const Container &itms) {
  auto printable_items =
      itms |
      views::transform(
          [](auto *itm) -> std::add_lvalue_reference_t<decltype(*itm)> { return *itm; });

  copy(printable_items, std::ostream_iterator<Item>(std::cout, "\n"));
}

int main(int argc, char *argv[]) {
  std::cout << "ok cpp 20 works with g++14" << std::endl;
  auto fav_stores = get_favorite_stores_for(get_current_customer_id());

  auto selected_items = get_all_featured_items(fav_stores);

  order_items_by_date_added(selected_items);

  render_item_gallery(selected_items);
  return 0;
}
